/********************************************************************
*  RayTraceCUDA.cu
*  This program ported to CUDA by noridon(NEW/Bio_100%).
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
//#include <GL/glut.h>
//#include <cuda_runtime.h>
//#include "Math//Tuple2.h"
//#include "Math//Tuple3.h"
//#include "Math//Tuple4.h"
//#include "Math//Matrix4.h"
///////////////////////////////////////////////////////////////////////
//// Please enable the below if you want to use only host.
////#define _CPU_
//
//#ifdef _CPU_
//#define __CONSTANT__
//#define __LOCAL_CALL__
//#define __GLOBAL_CALL__
//#else
//#define __CONSTANT__        __constant__
//#define __LOCAL_CALL__        __device__
//#define __GLOBAL_CALL__        __global__
//#endif
//
//#define        SCREEN_SX        (1024)
//#define SCREEN_SY        (768)
//
//const double ZOOM = 600;
//
//const float DATA1[] =
//{
//        20,40,20,
//        0,0,0,
//        -8,9,-3,
//        ZOOM * SCREEN_SY / 212
//};
//
//const int NUMOBJ = 6;
//
//struct tagOBJS
//{
//        float d[6];
//        int c[2];
//};
//
//__CONSTANT__ const tagOBJS objs[] =
//{
//        2,2,2,
//        .2,.2,.2,
//        1,2,
//        -2,2,2,
//        .2,.2,.2,
//        1,3,
//        -6,4,2,
//        .2,.2,.2,
//        1,4,
//        -2,2,-2,
//        .2,.2,.2,
//        1,5,
//        -6,4,-6,
//        .2,.2,.2,
//        1,6,
//        0,-2,0,
//        20,1,20,
//        0,-1
//};
//
//struct tagSHDS 
//{
//        float d[7];
//        int c[1];
//};
//
//__CONSTANT__ const tagSHDS shds[] = 
//{
//        .9,.9,.9,
//        .5,.4,.6,
//        .7,6,
//        .0,.9,.0,
//        .5,.4,.6,
//        .7,6,
//        .9,.0,.0,
//        .3,.6,.0,
//        .0,0,
//        .9,.9,.9,
//        .3,.6,.0,
//        .6,8,
//        .0,.0,.9,
//        .3,.6,.0,
//        .6,8,
//        .9,.9,.9,
//        .3,.6,.0,
//        .6,6,
//        .0,.0,.0,
//        .3,.6,1,
//        .9,8
//};
//
//typedef struct _tagscrn
//{
//        int        PX;
//        int        PY;
//}SCRN;
//
//void trace(SCRN scrn);
//void display();
//void reshape(int w, int h);
//void resize(int w, int h);
//void timer(int value);
//
///************************************************************************/
///* Init CUDA                                                            */
///************************************************************************/
//bool InitCUDA(void)
//{
//        int count = 0;
//        int i = 0;
//
//        cudaGetDeviceCount(&count);
//        if(count == 0)
//		{
//                fprintf(stderr, "There is no device.\n");
//                return false;
//        }
//
//        for(i = 0; i < count; i++)
//		{
//                cudaDeviceProp prop;
//                if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) 
//				{
//                        if(prop.major >= 1) 
//						{
//                                break;
//                        }
//                }
//        }
//        if(i == count)
//		{
//                fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
//                return false;
//        }
//        cudaSetDevice(i);
//        return true;
//}
//
//double m_ctime;
//
///////////////////////////////////////////////////////////////////////
//__CONSTANT__ const float ma=1000;
//__CONSTANT__ const float mi=1E-03;
//__CONSTANT__ const float pt = 4;
//__CONSTANT__ float VTbl[16];
//
//__LOCAL_CALL__ _inline float3 cross(float3 vector, const int n, float* pT, float3 cvec);
//__LOCAL_CALL__ _inline float3 shade(float3* pVector, float3 color, const int n, float3* pLvec, int* pF, float2* pRfrn, float* pT, float3 cvec, float* V);
//__LOCAL_CALL__ _inline float3 box(float3 vector, float3 rvec, float3 vabc, float* pT);
//__LOCAL_CALL__ _inline float3 ball(float3 vector, float3 nvec, float3 rvec, float3 vabc, float* pT);
//
//__LOCAL_CALL__ _inline float3 pixel(float3 vector, float3 color, float3* pCvec, float* V);
//__GLOBAL_CALL__ void Render(float3* pLineBuffer, int sy);
//
//__LOCAL_CALL__ _inline float3 pixel(float3 vector, float3 color, float3* pCvec, float* V)
//{
//        float2 rfrn = make_float2(1, 0);
//        float3 lvec = make_float3(0,0,0);
//        float3 cvec = *pCvec;
//        float  t = 0;
//        int n, f;
//        do {
//                double tt = ma;
//                int tn;
//                for (n = 0; n < NUMOBJ; ++n) {
//                        float3 nvec = cross(vector, n, &t, cvec);
//                        if (tt > t && t > mi) {
//                                tt = t; tn = n; lvec.x = nvec.x; lvec.y = nvec.y; lvec.z = nvec.z;
//                        }
//                }
//                if (tt == ma){
//                        *pCvec = cvec;
//                        return color;
//                }
//                cvec.x=cvec.x+tt*vector.x; cvec.y=cvec.y+tt*vector.y; cvec.z=cvec.z+tt*vector.z; n=tn;
//                color = shade(&vector, color, n, &lvec, &f, &rfrn, &t, cvec, V);
//        } while (f == 1);
//        *pCvec = cvec;
//        return color;
//}
//
//__LOCAL_CALL__ _inline float3 cross(float3 vector, const int n, float* pT, float3 cvec)
//{
//        float3 nvec = make_float3(0, 0, 0);
//        float3 rvec = make_float3(0, 0, 0);
//        float3 vabc = make_float3(0, 0, 0);
//        rvec.x =cvec.x-objs[n].d[0]; rvec.y=cvec.y-objs[n].d[1]; rvec.z=cvec.z-objs[n].d[2];
//        vabc.x=objs[n].d[3]; vabc.y=objs[n].d[4]; vabc.z=objs[n].d[5];
//        switch (objs[n].c[0]) {
//        case 0:
//                nvec = box(vector, rvec, vabc, pT);
//                break;
//        case 1:
//                nvec = ball(vector, nvec, rvec, vabc, pT);
//                break;
//        default:
//                break;
//        }
//        return nvec;
//}
//
//__LOCAL_CALL__ _inline float3 box(float3 vector, float3 rvec, float3 vabc, float* pT)
//{
//        float3 nvec = make_float3(0,0,0);
//
//        float t1, t2, t3;
//        if (vector.x == 0.0) t1 = ma;
//        else if (rvec.x < 0.0) t1=-(rvec.x+vabc.x)/vector.x;
//        else t1=-(rvec.x-vabc.x)/vector.x;
//        if (vector.y == 0.0) t2 = ma;
//        else if (rvec.y < 0.0) t2=-(rvec.y+vabc.y)/vector.y;
//        else t2=-(rvec.y-vabc.y)/vector.y;
//        if (vector.z == 0.0) t3 = ma;
//        else if (rvec.z < 0.0) t3=-(rvec.z+vabc.z)/vector.z;
//        else t3=-(rvec.z-vabc.z)/vector.z;
//        if (fabs(rvec.y+t1*vector.y)>vabc.y || fabs(rvec.z+t1*vector.z)>vabc.z) t1 = ma;
//        if (fabs(rvec.z+t2*vector.z)>vabc.z || fabs(rvec.x+t2*vector.x)>vabc.z) t2 = ma;
//        if (fabs(rvec.x+t3*vector.x)>vabc.x || fabs(rvec.y+t3*vector.y)>vabc.y) t3 = ma;
//        if (t1 <= t2 && t1 <= t3) { *pT=t1; nvec.x=-vector.x/fabs(vector.x); nvec.y=0; nvec.z=0; }
//        if (t2 <= t3 && t2 <= t1) { *pT=t2; nvec.y=-vector.y/fabs(vector.y); nvec.z=0; nvec.x=0; }
//        if (t3 <= t1 && t3 <= t2) { *pT=t3; nvec.z=-vector.z/fabs(vector.z); nvec.x=0; nvec.y=0; }
//        return nvec;
//}
//
//__LOCAL_CALL__ _inline float3 ball(float3 vector, float3 nvec, float3 rvec, float3 vabc, float* pT)
//{
//        float t1, t2;
//        double aa, bb, cc, dd;
//        aa=vector.x*vector.x*vabc.x+vector.y*vector.y*vabc.y+vector.z*vector.z*vabc.z;
//        bb=rvec.x*vector.x*vabc.x+rvec.y*vector.y*vabc.y+rvec.z*vector.z*vabc.z;
//        cc=rvec.x*rvec.x*vabc.x+rvec.y*rvec.y*vabc.y+rvec.z*rvec.z*vabc.z-1;
//        dd=bb*bb-aa*cc;
//        if (dd<0) { *pT = ma; return nvec;}
//        t1=(-bb-(float)sqrt(dd))/aa;t2=(-bb+(float)sqrt(dd))/aa;
//        if (t1<t2) *pT=t1; else *pT=t2;
//        nvec.x=vabc.x*(rvec.x+*pT*vector.x);nvec.y=vabc.y*(rvec.y+*pT*vector.y);nvec.z=vabc.z*(rvec.z+*pT*vector.z);
//        float _m = rsqrtf(nvec.x*nvec.x+nvec.y*nvec.y+nvec.z*nvec.z);
//        nvec.x *= _m; nvec.y *= _m; nvec.z *= _m;
//        return nvec;
//}
//
//__LOCAL_CALL__ _inline float3 shade(float3* pVector, float3 color, const int N, float3* pLvec, int* pF, float2* pRfrn, float* pT, float3 cvec, float* V)
//{
//        float3 lvec = *pLvec;
//        float3 vector = *pVector;
//        float2 rfrn = *pRfrn;
//        int f = *pF;
//        int n = N;
//        int px, py, pz;
//        float sr, sg, sb, sa, sd, sf, sp;
//        int se;
//        float jx, jy, jz;
//        float sm;
//        float vn, wx, wy, wz, sn;
//
//        int sh=objs[n].c[1];
//        if (sh == -1) {
//                px=(int)fabs(cvec.x+100)/pt-(cvec.x+100<0);
//                py=(int)fabs(cvec.y+100)/pt-(cvec.y+100<0);
//                pz=(int)fabs(cvec.z+100)/pt-(cvec.z+100<0);
//                sh=(px+py+pz)%2;
//        }
//        sr=shds[sh].d[0];sg=shds[sh].d[1];sb=shds[sh].d[2];
//        sa=shds[sh].d[3];sd=shds[sh].d[4];sf=shds[sh].d[5];
//        sp=shds[sh].d[6];se=shds[sh].c[0];
//        jx=V[12]-vector.x;jy=V[13]-vector.y;jz=V[14]-vector.z;
//        sm = (lvec.x*jx+lvec.y*jy+lvec.z*jz) * rsqrtf(jx*jx+jy*jy+jz*jz);
//        if (sm < 0) sm = 0;
//        for (int p = 1; p <= se; ++p) {
//                sm = sm * sm;
//        }
//        vn=-2*(lvec.x*vector.x+lvec.y*vector.y+lvec.z*vector.z);
//        wx=vector.x+vn*lvec.x;wy=vector.y+vn*lvec.y;wz=vector.z+vn*lvec.z;
//        vector.x=V[12];vector.y=V[13];vector.z=V[14];
//        sn=lvec.z*vector.x+lvec.y*vector.y+lvec.z*vector.z;
//        if (sn < 0) sn = 0;
//        for (n = 0; n < NUMOBJ; ++n) {
//                cross(vector, n, pT, cvec);
//                if (ma > *pT && *pT > mi) {
//                        sn = 0; sm = 0;
//                }
//        }
//        color.x = color.x+(sr*(sa+sd*sn)+sp*sm)*rfrn.x;
//        color.y = color.y+(sg*(sa+sd*sn)+sp*sm)*rfrn.x;
//        color.z = color.z+(sb*(sa+sd*sn)+sp*sm)*rfrn.x;
//        if (sf == 0 && rfrn.y < 4) {
//                f=0;
//                *pF = f;
//                return color;
//        }
//        f=1;rfrn.x=rfrn.x*sf;rfrn.y=rfrn.y+1;
//        vector.x=wx;vector.y=wy;vector.z=wz;
//        *pVector = vector;
//        *pLvec = lvec;
//        *pF = f;
//        *pRfrn = rfrn;
//        return color;
//}
//texture<int, 1, cudaReadModeElementType> rT1;
//texture<int, 1, cudaReadModeElementType> rT2;
//
//__device__ __inline float appSin(float Vaule)
//{
//	return sin(Vaule);
//}
//
//__device__ __inline float appCos(float Value)
//{
//	return cos(Value);
//}
//
//__global__ void AddVector(int* pDeviceA, int* pDeviceB, int* pDeviceC)
//{
//	int i = blockDim.x*blockIdx.x+threadIdx.x;
//	pDeviceC[i] = appSin((float)pDeviceA[i]) + appCos((float)pDeviceB[i]);
//}
//
//__global__ void AddVectorUseTexture(int* pDeviceC)
//{
//	int i = blockDim.x*blockIdx.x+threadIdx.x;
//	//pDeviceC[i] = appSin((float)tex1Dfetch(rT1, i))+appCos((float)tex1Dfetch(rT2, i));
//	pDeviceC[i] = tex1Dfetch(rT1, i);
//}


//int main()
//{
//	InitCUDA();
//	int ComputCount = 1024;
//	int ComputSize = 1024*sizeof(int);
//
//	cudaEvent_t start, stop;
//	cudaEventCreate(&start);
//	cudaEventCreate(&stop);
//	cudaEventRecord(start, 0);
//	int* h_A = (int*)malloc(ComputSize);
//	int* h_B = (int*)malloc(ComputSize);
//	int* h_C = (int*)malloc(ComputSize);
//	//memset(h_A, 1, ComputSize);
//	//memset(h_B, 2, ComputSize);
//	for(int i=0; i<ComputCount; i++)
//	{
//		h_A[i] = i/*3.14/4*/;
//		h_B[i] = 3.14/4;
//	}
//
//	int* d_A;
//	cudaMalloc((void**)&d_A, ComputSize);
//
//	int* d_B;
//	cudaMalloc((void**)&d_B, ComputSize);
//
//	int* d_C;
//	cudaMalloc((void**)&d_C, ComputSize);
//
//	cudaStream_t stream;
//	cudaStreamCreate(&stream);
//
//	//cudaMemcpyAsync(d_A, h_A, ComputSize, cudaMemcpyHostToDevice, stream);
//	//cudaMemcpyAsync(d_B, h_B, ComputSize, cudaMemcpyHostToDevice, stream);
//	cudaMemcpy(d_A, h_A, ComputSize, cudaMemcpyHostToDevice);
//	cudaMemcpy(d_B, h_B, ComputSize, cudaMemcpyHostToDevice);
//
//	cudaBindTexture(0, rT1, d_A );
//	cudaBindTexture(0, rT2, d_B );
//
//	int threadsPreBlock = 512;
//	int blocks = ComputCount/threadsPreBlock;
//	
//	//AddVector<<<blocks, threadsPreBlock>>>(d_A, d_B, d_C);
//	//AddVector<<<blocks, threadsPreBlock, 0, stream>>>(d_A, d_B, d_C);
//	AddVectorUseTexture<<<blocks, threadsPreBlock, 0, stream>>>(d_C);
//	
//	cudaMemcpy(h_C, d_C, ComputSize, cudaMemcpyDeviceToHost);
//	//cudaMemcpyAsync(h_C, d_C, ComputSize, cudaMemcpyDeviceToHost, stream);
//
//	cudaUnbindTexture(rT1);
//	cudaUnbindTexture(rT2);
//
//	cudaStreamSynchronize(stream);
//	cudaStreamDestroy(stream);
//
//	cudaFree(d_A);
//	cudaFree(d_B);
//	cudaFree(d_C);
//	cudaEventRecord(stop, 0);
//	cudaEventSynchronize(stop);
//	float elapsedTime;
//	cudaEventElapsedTime(&elapsedTime, start, stop);
//	
//	for(int i=0; i<ComputCount; i++)
//	{
//		printf("%d ", h_C[i]);
//	}
//	printf("\n %f", elapsedTime);
//	return 0;
//}
#include "./Scene//Scene.h"
#include <Windows.h>
#include <string>

int main(int argc, char* argv[])
{
	if(argc<2)
	{
		return 1;
	}

	InitCUDA();

	CSceneManager Manager;
	string Dir = argv[1];
	printf("SceneDesc File Path is:%s", Dir.c_str());

	cudaEvent_t start, stop;
	cudaEventCreate(&start);
	cudaEventCreate(&stop);
	cudaEventRecord(start, 0);

	Manager.PaserSceneFile(Dir);
	Manager.LoadStaticMesh();


	Manager.RasterAllTriangles();
	Manager.BuildCollisionData();
	
	Manager.PrepareDeviceData();
	Manager.PrepareConstantData();
	Manager.PrepareTextureBuffer();

	Manager.BuildLighting();

	cudaThreadSynchronize();
	cudaEventRecord(stop, 0);
	cudaEventSynchronize(stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime, start, stop);
	printf("\n costTime: %f ms", elapsedTime);

	Manager.DumpLightMaps();
	Manager.ReleaseTextures();
	Manager.ReleaseDeviceData();
	return 0;
}